﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArithmeticLibrary;

namespace Nolineq.Methods
{
    /// <summary>
    /// Resic rovnic Newtonovou damping metodou.
    /// </summary>
    public class NewtonsDampingMethod : EquationSolver
    {
        public NewtonsDampingMethod(Function function, double start, double end, double root, double precision, int maxSteps)
            : base(function, start, end, root, precision, maxSteps)
        {
            this.Name = "Newtonova damping metoda";
        }

        public override Solution Solve()
        {
            int steps = 0;

            // prvni aproximace
            double oldResult = Root;

            // dokud nename koren
            while (Math.Abs(Function.Evaluate(oldResult)) >= Precision && steps < MaxSteps)
            {
                // kontrola, jestli neni nulova derivace
                if (Function.Evaluate1stDerivation(oldResult) == 0)
                    // pokud ano, tak hod chybu
                    throw new UnsuitableMethodException("f'(" + oldResult + 
                        ") = 0 - hodnota první derivace v bodě je nulová. " +
                        "Zkuste upravit počáteční kořen.");

                // diference
                double d = Function.Evaluate(oldResult) / Function.Evaluate1stDerivation(oldResult);

                // nova aproximace
                double newResult = oldResult - d;

                double alfa = 1;

                int flag = 0;

                // dokud neni nova aproximace prijatelna
                while (!FunctionMonitor(newResult, oldResult) && flag < 10)
                {
                    alfa = alfa / 2;
                    newResult = oldResult - alfa * d;
                    ++flag;
                }

                // pokud jsme nenasli za 10 kroku dobrou aproximaci
                if (flag == 10)
                    // hodim chybu
                    throw new UnsuitableMethodException("Tato metoda neni schopna podle zadaných vstupních parametrů žádný kořen. " + 
                        "Zkuste zadat jiné parametry nebo zvolit jinou metodu.");

                // oznacim novou aproximaci jako starou
                oldResult = newResult;

                ++steps;
            }

            // kontrola prekroceni limitu iteraci
            if (steps == MaxSteps)
                throw new UnsuitableMethodException("Překročen limit počtu iterací. Zkuste nastavit větší počet iterací, upravit počáteční parametry nebo zvolte jinou metodu.");
            
            return new Solution(oldResult, steps);
        }

        /// <summary>
        /// Zjisti, zda je nova aproximace dost dobra pro pokracovani.
        /// </summary>
        /// <param name="newResult">Nova aproximace.</param>
        /// <param name="oldResult">Posledni aproximace.</param>
        /// <returns>Vrati TRUE, pokud je nova aproximace dobra.</returns>
        private bool FunctionMonitor(double newResult, double oldResult)
        {
            try
            {
                if (Math.Abs(Function.Evaluate(newResult) / Function.Evaluate(oldResult)) < 1)
                    return true;
                else
                    return false;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
